package com.eva.ss;

import com.eva.framework.common.constants.ResponseStatus;
import com.eva.framework.common.exception.BusinessException;
import com.eva.framework.rbac.model.RbacUserInfo;
import com.eva.framework.security.utils.SecurityUtil;
import jakarta.annotation.Resource;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

/**
 * 用户名和密码认证器，实现用户名和密码的认证逻辑
 * 包括密码比对、账户是否过期、账户是否锁定、账户是否可用等层面的处理。
 */
@Component
public class UsernamePasswordAuthenticationProvider implements AuthenticationProvider {

    @Resource
    private UserDetailsServiceImpl userDetailsService;

    /**
     * 自定义实现认证逻辑
     *
     * @param authentication 认证对象，包含了登录用户名和密码等信息
     * @return Authentication
     * @throws AuthenticationException 认证异常
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        // 根据用户名查询用户信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (userDetails == null) {
            throw new BusinessException(ResponseStatus.ACCOUNT_INCORRECT);
        }

        // 密码验证
        RbacUserInfo loginUserInfo = (RbacUserInfo) userDetails;
        if (!SecurityUtil.encryptPassword(password, loginUserInfo.getSalt()).equals(loginUserInfo.getPassword())) {
            throw new BusinessException(ResponseStatus.ACCOUNT_INCORRECT);
        }

        return new UsernamePasswordAuthenticationToken(
                userDetails, password, userDetails.getAuthorities());
    }

    /**
     * 是否支持认证，如果不支持，将有其它认证器继续认证，以实现多种认证方式
     *
     * @param authentication 认证类
     * @return boolean 是否支持认证
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
